home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / innd / ng.c < prev    next >
C/C++ Source or Header  |  1993-03-18  |  10KB  |  389 lines

  1. /*  $Revision: 1.19 $
  2. **
  3. **  Routine for the in-core data structures for the active and newsfeeds
  4. **  files.
  5. */
  6. #include "innd.h"
  7. #include "mydir.h"
  8.  
  9.  
  10. /*
  11. **  Hash function taken from Chris Torek's hash package posted to
  12. **  comp.lang.c on 18-Oct-90 in <27038@mimsy.umd.edu>.  Thanks, Chris.
  13. */
  14. #define NGH_HASH(Name, p, j)    \
  15.     for (p = Name, j = 0; *p; ) j = (j << 5) + j + *p++
  16.  
  17.  
  18. /*
  19. **  Size of hash table.   Change NGH_BUCKET if not a power of two.
  20. */
  21. #define NGH_SIZE    512
  22. #define NGH_BUCKET(j)    &NGHtable[j & (NGH_SIZE - 1)]
  23.  
  24.  
  25. /*
  26. **  Newsgroup hash entry, which is really a hash bucket -- pointers
  27. **  to all the groups with this hash code.
  28. */
  29. typedef struct _NGHASH {
  30.     int            Size;
  31.     int            Used;
  32.     NEWSGROUP        **Groups;
  33. } NGHASH;
  34.  
  35.  
  36. STATIC BUFFER    NGdirs;
  37. STATIC BUFFER    NGnames;
  38. STATIC NGHASH    NGHtable[NGH_SIZE];
  39. STATIC int    NGHbuckets;
  40. STATIC int    NGHcount;
  41.  
  42.  
  43.  
  44. /*
  45. **  Sorting predicate for qsort call in NGparsefile.  Put newsgroups in
  46. **  rough order of their activity.  Will be better if we write a "counts"
  47. **  file sometime.
  48. */
  49. STATIC int
  50. NGcompare(p1, p2)
  51.     POINTER    p1;
  52.     POINTER    p2;
  53. {
  54.     NEWSGROUP    **ng1;
  55.     NEWSGROUP    **ng2;
  56.  
  57.     ng1 = CAST(NEWSGROUP**, p1);
  58.     ng2 = CAST(NEWSGROUP**, p2);
  59.     return ng1[0]->Last - ng2[0]->Last;
  60. }
  61.  
  62.  
  63. /*
  64. **  Convert a newsgroup name into a directory name.
  65. */
  66. STATIC void
  67. NGdirname(p)
  68.     register char    *p;
  69. {
  70.     for ( ; *p; p++)
  71.     if (*p == '.')
  72.         *p = '/';
  73. }
  74.  
  75.  
  76. /*
  77. **  Parse a single line from the active file, filling in ngp.  Be careful
  78. **  not to write NUL's into the in-core copy, since we're either mmap(2)'d,
  79. **  or we want to just blat it out to disk later.
  80. */
  81. STATIC BOOL
  82. NGparseentry(ngp, p, end)
  83.     register NEWSGROUP        *ngp;
  84.     register char        *p;
  85.     register char        *end;
  86. {
  87.     register char        *q;
  88.     register unsigned int    j;
  89.     register NGHASH        *htp;
  90.     register NEWSGROUP        **ngpp;
  91.     register int        i;
  92.  
  93.     if ((q = strchr(p, ' ')) == NULL)
  94.     return FALSE;
  95.     i = q - p;
  96.  
  97.     ngp->NameLength = i;
  98.     ngp->Name = &NGnames.Data[NGnames.Used];
  99.     (void)strncpy(ngp->Name, p, (SIZE_T)i);
  100.     ngp->Name[i] = '\0';
  101.     NGnames.Used += i + 1;
  102.  
  103.     ngp->Dir = &NGdirs.Data[NGdirs.Used];
  104.     (void)strncpy(ngp->Dir, p, (SIZE_T)i);
  105.     ngp->Dir[i] = '\0';
  106.     NGdirs.Used += i + 1;
  107.     NGdirname(ngp->Dir);
  108.  
  109.     ngp->LastString = ++q;
  110.     if ((q = strchr(q, ' ')) == NULL || q > end)
  111.     return FALSE;
  112.     ngp->Lastwidth = q - ngp->LastString;
  113.     if ((q = strchr(q, ' ')) == NULL || q > end)
  114.     return FALSE;
  115.     if ((q = strchr(q + 1, ' ')) == NULL || q > end)
  116.     return FALSE;
  117.     ngp->Rest = ++q;
  118.     /* We count on atoi() to stop at the space after the digits! */
  119.     ngp->Last = atol(ngp->LastString);
  120.     ngp->nSites = 0;
  121.     ngp->Sites = NEW(int, NGHcount);
  122.     ngp->Alias = NULL;
  123.  
  124.     /* Find the right bucket for the group, make sure there is room. */
  125.     /* SUPPRESS 6 *//* Over/underflow from plus expression */
  126.     NGH_HASH(ngp->Name, p, j);
  127.     htp = NGH_BUCKET(j);
  128.     for (p = ngp->Name, ngpp = htp->Groups, i = htp->Used; --i >= 0; ngpp++)
  129.     if (*p == ngpp[0]->Name[0] && EQ(p, ngpp[0]->Name)) {
  130.         syslog(L_ERROR, "%s duplicate_group %s", LogName, p);
  131.         return FALSE;
  132.     }
  133.     if (htp->Used >= htp->Size) {
  134.     htp->Size += NGHbuckets;
  135.     RENEW(htp->Groups, NEWSGROUP*, htp->Size);
  136.     }
  137.     htp->Groups[htp->Used++] = ngp;
  138.  
  139.     return TRUE;
  140. }
  141.  
  142.  
  143. /*
  144. **  Parse the active file, building the initial Groups global.
  145. */
  146. void
  147. NGparsefile()
  148. {
  149.     register char    *p;
  150.     register char    *q;
  151.     register int    i;
  152.     register BOOL    SawMe;
  153.     register NEWSGROUP    *ngp;
  154.     register NGHASH    *htp;
  155.     char        **strings;
  156.     char        *active;
  157.     char        *end;
  158.  
  159.     /* If re-reading, remove anything we might have had. */
  160.     if (Groups) {
  161.     for (i = nGroups, ngp = Groups; --i >= 0; ngp++)
  162.         DISPOSE(ngp->Sites);
  163.     DISPOSE(Groups);
  164.     DISPOSE(GroupPointers);
  165.     DISPOSE(NGdirs.Data);
  166.     DISPOSE(NGnames.Data);
  167.     }
  168.  
  169.  
  170.     /* Get active file and space for group entries. */
  171.     active = ICDreadactive(&end);
  172.     for (p = active, i = 0; p < end && (p = strchr(p, '\n')) != NULL; p++, i++)
  173.     continue;
  174.     nGroups = i;
  175.     Groups = NEW(NEWSGROUP, nGroups);
  176.     GroupPointers = NEW(NEWSGROUP*, nGroups);
  177.  
  178.     /* Get space to hold copies of the names and the directory names.
  179.      * This might take more space than individually allocating each
  180.      * element, but it is definitely easier on the system. */
  181.     i = end - active;
  182.     NGdirs.Size = i;
  183.     NGdirs.Data = NEW(char, NGdirs.Size + 1);
  184.     NGdirs.Used = 0;
  185.     NGnames.Size = i;
  186.     NGnames.Data = NEW(char, NGnames.Size + 1);
  187.     NGnames.Used = 0;
  188.  
  189.     /* Set up the default hash buckets. */
  190.     NGHbuckets = nGroups / NGH_SIZE;
  191.     if (NGHbuckets == 0)
  192.     NGHbuckets = 1;
  193.     if (NGHtable[0].Groups)
  194.     for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++)
  195.         htp->Used = 0;
  196.     else
  197.     for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++) {
  198.         htp->Size = NGHbuckets;
  199.         htp->Groups = NEW(NEWSGROUP*, htp->Size);
  200.         htp->Used = 0;
  201.     }
  202.  
  203.     /* Count the number of sites. */
  204.     SawMe = FALSE;
  205.     for (strings = SITEreadfile(TRUE), i = 0; (p = strings[i]) != NULL; i++)
  206.     if (*p == 'M' && *++p == 'E' && *++p == ':')
  207.         SawMe = TRUE;
  208.     if (i == 0 || (i == 1 && SawMe)) {
  209.     syslog(L_ERROR, "%s bad_newsfeeds no feeding sites", LogName);
  210.     NGHcount = 1;
  211.     }
  212.     else
  213.     NGHcount = i;
  214.  
  215.     /* Loop over all lines in the active file, filling in the fields of
  216.      * the Groups array. */
  217.     for (p = active, ngp = Groups, i = nGroups; --i >= 0; ngp++, p = q + 1) {
  218.     ngp->Start = p - active;
  219.     if ((q = strchr(p, '\n')) == NULL || !NGparseentry(ngp, p, q)) {
  220.         syslog(L_FATAL, "%s bad_active %s...", LogName, MaxLength(p, p));
  221.         exit(1);
  222.     }
  223.     }
  224.  
  225.     /* Sort each bucket. */
  226.     for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++)
  227.     if (htp->Used > 1)
  228.         qsort((POINTER)htp->Groups, (SIZE_T)htp->Used,
  229.         sizeof htp->Groups[0], NGcompare);
  230.  
  231.     /* Chase down any alias flags. */
  232.     for (ngp = Groups, i = nGroups; --i >= 0; ngp++)
  233.     if (ngp->Rest[0] == NF_FLAG_ALIAS) {
  234.         ngp->Alias = ngp;
  235.         if ((p = strchr(ngp->Alias->Rest, '\n')) != NULL)
  236.         *p = '\0';
  237.         ngp->Alias = NGfind(&ngp->Alias->Rest[1]);
  238.         if (p)
  239.         *p = '\n';
  240.         if (ngp->Alias != NULL && ngp->Alias->Rest[0] == NF_FLAG_ALIAS)
  241.         syslog(L_NOTICE, "%s alias_error %s too many levels",
  242.             LogName, ngp->Name);
  243.     }
  244. }
  245.  
  246.  
  247. /*
  248. **  Hash a newsgroup and see if we get it.
  249. */
  250. NEWSGROUP *
  251. NGfind(Name)
  252.     char            *Name;
  253. {
  254.     register char        *p;
  255.     register int        i;
  256.     register unsigned int    j;
  257.     register NEWSGROUP        **ngp;
  258.     char            c;
  259.     NGHASH            *htp;
  260.  
  261.     /* SUPPRESS 6 *//* Over/underflow from plus expression */
  262.     NGH_HASH(Name, p, j);
  263.     htp = NGH_BUCKET(j);
  264.     for (c = *Name, ngp = htp->Groups, i = htp->Used; --i >= 0; ngp++)
  265.     if (c == ngp[0]->Name[0] && EQ(Name, ngp[0]->Name))
  266.         return ngp[0];
  267.     return NULL;
  268. }
  269.  
  270.  
  271. /*
  272. **  Split a newsgroups header line into the groups we get.  Return a
  273. **  point to static memory and clobber the argument along the way.
  274. */
  275. char **
  276. NGsplit(p)
  277.     register char    *p;
  278. {
  279.     static char        **groups;
  280.     static int        oldlength;
  281.     register char    **gp;
  282.     register int    i;
  283.  
  284.     /* Get an array of character pointers. */
  285.     i = strlen(p);
  286.     if (groups == NULL) {
  287.     groups = NEW(char*, i + 1);
  288.     oldlength = i;
  289.     }
  290.     else if (oldlength < i) {
  291.     RENEW(groups, char*, i + 1);
  292.     oldlength = i;
  293.     }
  294.  
  295.     /* Loop over text. */
  296.     for (gp = groups; *p; *p++ = '\0') {
  297.     /* Skip leading separators. */
  298.     for (; NG_ISSEP(*p); p++)
  299.         continue;
  300.     if (*p == '\0')
  301.         break;
  302.  
  303.     /* Mark the start of the newsgroup, move to the end of it. */
  304.     for (*gp++ = p; *p && !NG_ISSEP(*p); p++)
  305.         continue;
  306.     if (*p == '\0')
  307.         break;
  308.     }
  309.     *gp = NULL;
  310.     return groups;
  311. }
  312.  
  313.  
  314. /*
  315. **  Renumber a group.
  316. */
  317. BOOL
  318. NGrenumber(ngp)
  319.     NEWSGROUP        *ngp;
  320. {
  321.     static char        NORENUMBER[] = "%s cant renumber %s %s too wide";
  322.     static char        RENUMBER[] = "%s renumber %s %s from %ld to %ld";
  323.     register DIR    *dp;
  324.     register DIRENTRY    *ep;
  325.     register char    *f2;
  326.     register char    *p;
  327.     char        *f3;
  328.     char        *f4;
  329.     char        *start;
  330.     long        l;
  331.     long        himark;
  332.     long        lomark;
  333.     char        *dummy;
  334.  
  335.     /* Get a valid offset into the active file. */
  336.     if (ICDneedsetup) {
  337.     syslog(L_ERROR, "%s unsynched must reload before renumber", LogName);
  338.     return FALSE;
  339.     }
  340.     start = ICDreadactive(&dummy) + ngp->Start;
  341.  
  342.     /* Check the file format. */
  343.     if ((f2 = strchr(start, ' ')) == NULL
  344.      || (f3 = strchr(++f2, ' ')) == NULL
  345.      || (f4 = strchr(++f3, ' ')) == NULL) {
  346.     syslog(L_ERROR, "%s bad_format active %s",
  347.         LogName, MaxLength(start, start));
  348.     return FALSE;
  349.     }
  350.     himark = atol(f2);
  351.     lomark = himark + 1;
  352.  
  353.     /* Scan the directory. */
  354.     if ((dp = opendir(ngp->Dir)) != NULL) {
  355.     while ((ep = readdir(dp)) != NULL) {
  356.         p = ep->d_name;
  357.         if (!CTYPE(isdigit, p[0]) || strspn(p, "0123456789") != strlen(p)
  358.          || (l = atol(p)) == 0)
  359.         continue;
  360.         if (l < lomark)
  361.         lomark = l;
  362.         if (l > himark)
  363.         himark = l;
  364.     }
  365.     (void)closedir(dp);
  366.     }
  367.     l = atol(f2);
  368.     if (himark != l) {
  369.     syslog(L_NOTICE, RENUMBER, LogName, ngp->Name, "hi", l, himark);
  370.     if (!FormatLong(f2, himark, f3 - f2 - 1)) {
  371.         syslog(L_NOTICE, NORENUMBER, LogName, ngp->Name, "hi");
  372.         return FALSE;
  373.     }
  374.     ngp->Last = himark;
  375.     ICDactivedirty++;
  376.     }
  377.     l = atol(f3);
  378.     if (lomark != l) {
  379.     if (lomark < l)
  380.         syslog(L_NOTICE, RENUMBER, LogName, ngp->Name, "lo", l, lomark);
  381.     if (!FormatLong(f3, lomark, f4 - f3)) {
  382.         syslog(L_NOTICE, NORENUMBER, LogName, ngp->Name, "lo");
  383.         return FALSE;
  384.     }
  385.     ICDactivedirty++;
  386.     }
  387.     return TRUE;
  388. }
  389.